using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RhythmComet.Physics;
using RhythmComet.Utils;

namespace RhythmComet.Particles
{
    public class ParticleSystem : DrawableGameComponent
    {
        private RhythmCometGame game;
        private IForce3D force;
        private VertexDeclaration vertexDeclaration;

        private ParticleCollection particles;
        private int howManyEffects;
        private int firstInactiveParticle;

        private Effect effect;
        private Matrix viewMatrix;
        private Matrix projectionMatrix;
        private Matrix worldMatrix;

        private Texture2D texture;
        private ParticleSystemSettings settings;
        private Color currentColor;

        public ParticleSystemSettings Settings
        {
            get { return settings; }
        }

        private bool dead;
        public bool Dead
        {
            get { return dead; }
        }
        //private string textureFilename;

        //private int minNumParticles;
        //private int maxNumParticles;
        //private float minInitialSpeed;
        //private float maxInitialSpeed;
        //private float minAcceleration;
        //private float maxAcceleration;
        //private float minRotationSpeed;
        //private float maxRotationSpeed;
        //private float minLifetime;
        //private float maxLifetime;
        //private float minScale;
        //private float maxScale;

        public int TotalParticles
        {
            get { return firstInactiveParticle; }
        }

        public ParticleSystem(RhythmCometGame game, int howManyEffects, ParticleSystemSettings settings)
            : base(game)
        {
            this.force = new DirectionalForce3D(0.0f, Vector3.Zero);
            this.game = game;
            this.howManyEffects = howManyEffects;
            this.settings = settings;
        }

        public ParticleSystem(RhythmCometGame game, int howManyEffects, IForce3D force, ParticleSystemSettings settings)
            : base(game)
        {
            this.force = force;
            this.game = game;
            this.howManyEffects = howManyEffects;
            this.settings = settings;
        }

        public void AddParticles(Vector2 where)
        {
            int numParticles = RandomGenerator.Random.Next(settings.minNumParticles, settings.maxNumParticles);

            int newLastNotActiveParticle = firstInactiveParticle + numParticles;

            for (int i = firstInactiveParticle; i < newLastNotActiveParticle; i++)
            {
                InitializeParticle(i, where);
            }

            firstInactiveParticle = newLastNotActiveParticle;
        }

        private void InitializeCamera()
        {
            projectionMatrix = game.Camera.Projection;
            viewMatrix = game.Camera.View;
            worldMatrix = Matrix.Identity;
        }

        private Vector2 PickRandomDirection()
        {
            float angle = RandomGenerator.RandomBetween(0.0f, MathHelper.TwoPi);
            return new Vector2((float)Math.Cos(angle), (float)Math.Sin(angle));
        }

        // para ser usado apenas no cometa
        private void InitializeConstants()
        {
           this.currentColor = Color.Red;
        }

        private void InitializeParticles()
        {
            this.particles = new ParticleCollection(this.howManyEffects);
            this.firstInactiveParticle = 0;
        }

        private void InitializeParticle(int index, Vector2 where)
        {
            Vector2 direction = PickRandomDirection();

            float velocity = RandomGenerator.RandomBetween(settings.minInitialSpeed, settings.maxInitialSpeed);
            float acceleration = RandomGenerator.RandomBetween(settings.minAcceleration, settings.maxAcceleration);
            float lifetime = RandomGenerator.RandomBetween(settings.minLifetime, settings.maxLifetime);
            float scale = RandomGenerator.RandomBetween(settings.minScale, settings.maxScale);
            float rotationSpeed = RandomGenerator.RandomBetween(settings.minRotationSpeed, settings.maxRotationSpeed);

            lifetime *= 0.2f;

            particles.SetPosition(index, new Vector3(where, 0.0f));
            particles.SetVelocity(index, new Vector3(velocity * direction, 0.0f));
            particles.SetAcceleration(index, new Vector3(acceleration * direction, 0.0f));
            particles.SetLifeTime(index, lifetime);
            particles.SetSize(index, 10);
            particles.SetRotationSpeed(index, rotationSpeed);
            particles.SetTimeSinceStart(index, 0.0f);
            particles.SetColor(index, this.currentColor);
        }

        public override void Initialize()
        {
            //InitializeConstants();
            InitializeParticles();
            InitializeCamera();

            base.Initialize();
        }

        public Color CurrentColor
        {
            get { return currentColor; }
            set { currentColor = value; }
        }

        public override void Update(GameTime gameTime)
        {
            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;

            for (int i = firstInactiveParticle - 1; i >= 0; i--)
            {
                    particles.SetTimeSinceStart(i, dt + particles.GetTimeSinceStart(i));
                    if (CheckForInactiveParticle(i))
                    {
                        continue;
                    }

                force.ApplyForce(particles, i, dt);
            }
            // apenas testando
            if (firstInactiveParticle == 0)
                dead = true;
            base.Update(gameTime);
        }

        public bool CheckForInactiveParticle(int index)
        {
            if (particles.GetLifeTime(index) < particles.GetTimeSinceStart(index))
            {
                particles.Copy(--firstInactiveParticle, index);
                return true;
            }
            return false;
        }

        public override void Draw(GameTime gameTime)
        {
            if (firstInactiveParticle > 0)
            {
                GraphicsDevice.VertexDeclaration = vertexDeclaration;

                effect.Parameters["World"].SetValue(worldMatrix);
                effect.Parameters["View"].SetValue(viewMatrix);
                effect.Parameters["Projection"].SetValue(projectionMatrix);
                effect.Parameters["ColorMap"].SetValue(texture);
                effect.CurrentTechnique = effect.Techniques[0];

                effect.Begin();
                effect.CurrentTechnique.Passes[0].Begin();
                game.GraphicsDevice.DrawUserPrimitives<ParticleVertex>(PrimitiveType.PointList, particles.Vertices, 0, firstInactiveParticle);
                effect.CurrentTechnique.Passes[0].End();
                effect.End();
            }

            base.Draw(gameTime);
        }

        protected override void LoadContent()
        {
            vertexDeclaration = new VertexDeclaration(game.GraphicsDevice, ParticleVertex.VertexElements);
            game.GraphicsDevice.VertexDeclaration = vertexDeclaration;
            game.GraphicsDevice.RenderState.DepthBufferEnable = true;
            game.GraphicsDevice.RenderState.DepthBufferWriteEnable = true;

            Effect tempEffect = game.Content.Load<Effect>(@"effects\particleEffect");
            effect = tempEffect.Clone(game.GraphicsDevice);

            texture = game.Content.Load<Texture2D>(settings.textureFilename);

            base.LoadContent();
        }
    }
}

